﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM
{
    #region Using Statements

    using System.Text.RegularExpressions;
    using System;
    using KLF.Lib.Browser.DOM.HTML;

    #endregion

    /// <summary>
    /// 
    /// </summary>
    public class DocumentType : Node
    {
        #region Constructors

        protected DocumentType()
        {
        }

        internal DocumentType(Document owner, string _rawTypeDeclaration)
            : base(owner)
        {
            Regex docTypeRe = new Regex(@"<!DOCTYPE (?<docTypeName>[^\s\&\%\"">]+){1}\s*"
                + @"(?<docPubSys>PUBLIC|SYSTEM){0,1}\s*"
                + @"(""(?<docUriOrID>[^""]+)""){0,1}\s*"
                + @"(""(?<docUri>[^""]+)""){0,1}>", RegexOptions.IgnoreCase);

            DocType = typeof(HTMLDocument);

            if (docTypeRe.IsMatch(_rawTypeDeclaration))
            {
                Match m = docTypeRe.Match(_rawTypeDeclaration);

                name = m.Groups["docTypeName"].Value.ToLowerInvariant();

                if (String.IsNullOrEmpty(m.Groups["docTypeName"].Value) ||
                    m.Groups["docTypeName"].Value.ToLowerInvariant().Equals("html"))
                {
                    if (!String.IsNullOrEmpty(m.Groups["docUriOrID"].Value))
                    {
                        publicId = m.Groups["docUriOrID"].Value;
                    }
                    if (!String.IsNullOrEmpty(m.Groups["docUriOrID"].Value))
                    {
                        systemId = m.Groups["docUri"].Value;
                    }

                    if (!String.IsNullOrEmpty(systemId) &&
                        systemId.ToLowerInvariant().Contains("xhtml"))
                    {
                        DocType = typeof(XHTMLDocument);
                    }
                    else
                    {
                        DocType = typeof(HTMLDocument);
                    }
                }
            }
        }

        #endregion

        #region Properties

        internal Type DocType { get; set; }

        #endregion

        #region DOM Level 1 Compliance

        public override Node cloneNode(bool deep)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// A NamedNodeMap containing the general entities, both external 
        /// and internal, declared in the DTD. Duplicates are discarded.
        /// </summary>
        public NamedNodeMap entities
        {
            get
            {
                return new NamedNodeMap();
            }
        }

        /// <summary>
        /// The name of DTD; i.e., the name immediately following the DOCTYPE keyword.
        /// </summary>
        public string name { get; internal set; }

        public override string nodeName
        {
            get { return name; }
        }

        public override string nodeValue
        {
            get { return null; }
        }

        public override byte nodeType
        {
            get { return DOCUMENT_TYPE_NODE; }
        }

        /// <summary>
        /// A NamedNodeMap containing the notations declared in the DTD. 
        /// Duplicates are discarded. Every node in this map also 
        /// implements the Notation interface.
        /// 
        /// The DOM Level 1 does not support editing notations, therefore 
        /// notations cannot be altered in any way.
        /// </summary>
        public NamedNodeMap notations
        {
            get
            {
                return new NamedNodeMap();
            }
        }

        #endregion

        #region DOM Level 2 Compliance

        /// <summary>
        /// The internal subset as a string, or null if there is none. This is 
        /// does not contain the delimiting square brackets.
        /// </summary>
        public string internalSubset
        {
            get
            {
                return _internalSubset != null ? _internalSubset.Value : null;
            }
            internal set
            {
                _internalSubset = new StringLookup(value);
            }
        }
        internal StringLookup _internalSubset;

        /// <summary>
        /// The public identifier of the external subset.
        /// </summary>
        public string publicId
        {
            get
            {
                return _publicId != null ? _publicId.Value : "";
            }
            internal set
            {
                _publicId = new StringLookup(value);
            }
        }
        internal StringLookup _publicId;

        /// <summary>
        /// The system identifier of the external subset. 
        /// This may be an absolute URI or not.
        /// </summary>
        public string systemId
        {
            get
            {
                return _systemId != null ? _systemId.Value : "";
            }
            internal set
            {
                _systemId = new StringLookup(value);
            }
        }
        internal StringLookup _systemId;

        #endregion

        #region Non-Standard DOM Methods

        internal virtual Document CreateDocument()
        {
            Document result = (Document) Activator.CreateInstance(DocType);

            result._DocumentType = this;

            return result;
        }

        internal static DocumentType Create(string qualifiedName, string publicId, string systemId)
        {
            DocumentType returnValue = new DocumentType()
            {
                name = qualifiedName,
                publicId = publicId,
                systemId = systemId
            };

            if (!String.IsNullOrEmpty(systemId) &&
                systemId.ToLowerInvariant().Contains("xhtml"))
            {
                returnValue.DocType = typeof(XHTMLDocument);
            }
            else
            {
                returnValue.DocType = typeof(HTMLDocument);
            }

            return returnValue;
        }

        #endregion
    }
}
